package trading_strategy.events.filtered;

import org.apache.log4j.Logger;
import trading_strategy.events.AbstractMarketEvent;
import trading_strategy.events.InstrumentEvent;
import trading_strategy.instruments.ListedInstrument;

import java.util.LinkedList;

/**
 * DMI as described in http://fxtrade.oanda.com/learn/graphs/indicators/adx#atr
 * <p/>
 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 6 mai 2010
 * Time: 12:05:58
 * To change this template use File | Settings | File Templates.
 */
public class DMIFilter extends DerivedSignal {
    public static final Logger LOGGER = Logger.getLogger(DMIFilter.class);

    public static class IndicatorPoint {
        double tr;
        double plusDM;
        double minusDM;
        double DI;
        double DX;

        IndicatorPoint(double tr, double plusDM, double minusDM) {
            this.tr = tr;
            this.plusDM = plusDM;
            this.minusDM = minusDM;
        }

        public double getTr() {
            return tr;
        }

        public double getPlusDM() {
            return plusDM;
        }

        public double getMinusDM() {
            return minusDM;
        }

        public double getDmi() {
            return (plusDM - minusDM) * 100d;
        }

        public double getDI() {
            return DI;
        }

        public double getDX() {
            return DX;
        }
    }

    public static class DMIPoint {
        double plusDM;
        double minusDM;

        DMIPoint(double plusDM, double minusDM) {
            this.plusDM = plusDM;
            this.minusDM = minusDM;
        }

        public double getDmi() {
            return (plusDM - minusDM) * 100d / (plusDM + minusDM);
        }
    }

    public class DMIEvent extends InstrumentEvent {
        DMIPoint point;

        DMIEvent(ListedInstrument instrument, DMIPoint point, long ts) {
            super(instrument, ts);
            this.point = point;
        }

        public DMIPoint getPoint() {
            return point;
        }
    }

    LinkedList<Candle> candles = new LinkedList<Candle>();
    LinkedList<IndicatorPoint> points = new LinkedList<IndicatorPoint>();
    int nbOfIntervals = 14;
    CandleFilter candleFilter;

    public DMIFilter(ListedInstrument instrument, String name, int nbOfIntervals, CandleFilter candleFilter) {
        super(name, "FilteredSignalDispatcher", instrument, candleFilter);
        this.instrument = instrument;
        this.nbOfIntervals = nbOfIntervals;
        this.candleFilter = candleFilter;
    }

    @Override
    public void onMarketEvent(AbstractMarketEvent event) {
        if (event instanceof CandleFilter.CandleEvent) {
            CandleFilter.CandleEvent ce = (CandleFilter.CandleEvent) event;
            Candle candle = ce.getCandle();
            if (candles.size() > nbOfIntervals) {
                Candle out = candles.pop();
            }
            // Check that there is no gap in the candles
            if (candles.size() > 0) {
                Candle last = candles.getLast();
                long gapMs = candle.getStart().getTime() - last.getEnd().getTime();
                long lastDuration = last.getEnd().getTime() - last.getStart().getTime();
                if (gapMs > lastDuration / 5) {
                    candles.clear();
                    points.clear();
                    LOGGER.warn("Gap in candle signal, resetting");
                    return;
                }
            }

            candles.add(candle);
            if (candles.size() > 1) {

                Candle prevCandle = candles.get(candles.size() - 2);

                double A = candle.getHigh() - prevCandle.getHigh();
                double B = prevCandle.getLow() - candle.getLow();
                double tr = Math.max(Math.max(Math.abs(candle.getHigh() - candle.getLow()),
                        //Math.abs(candle.getHigh() - prevCandle.getClose())),
                        candle.getHigh() - prevCandle.getClose()),
                        //Math.abs(prevCandle.getClose() - candle.getLow()));
                        prevCandle.getClose() - candle.getLow());

                double minusDM = 0;
                double plusDM = 0;
                if (A > 0 || B > 0) {
                    if (A > B) {
                        plusDM = A;
                    } else if (A < B) {
                        minusDM = B;
                    }
                }

                IndicatorPoint p = new IndicatorPoint(tr, plusDM, minusDM);
                if (points.size() > nbOfIntervals) {
                    points.pop();
                }
                points.add(p);

                if (points.size() >= nbOfIntervals) {
                    double plusDMAverage = 0;
                    double minusDMAverage = 0;
                    double trAverage = 0;

                    for (IndicatorPoint point : points) {
                        plusDMAverage += point.plusDM;
                        minusDMAverage += point.minusDM;
                        trAverage += point.tr;
                    }

                    plusDMAverage /= (double) nbOfIntervals;
                    minusDMAverage /= (double) nbOfIntervals;
                    trAverage /= (double) nbOfIntervals;

                    double PDI = plusDMAverage / trAverage;
                    double NDI = minusDMAverage / trAverage;

                    double DIDiff = Math.abs(PDI - NDI);
                    double DX = DIDiff / (PDI + NDI);

                    DMIEvent e = new DMIEvent(instrument, new DMIPoint(PDI, NDI), event.getTs());
                    addEvent(e);
                }
            }
        }
    }
}